package com.thinkit.bigdata.web.controller;

import com.thinkit.bigdata.core.ConfigHelper;
import com.thinkit.bigdata.core.util.FtpUtil;
import com.thinkit.bigdata.core.util.RSAUtil;
import com.thinkit.bigdata.core.util.StringUtil;
import com.thinkit.bigdata.web.controller.pubmet.SessionManage;
import com.thinkit.bigdata.web.model.SmsCodeBean;
import com.thinkit.bigdata.web.model.result.Constants;
import com.thinkit.bigdata.web.model.result.HtmlResult;
import com.thinkit.bigdata.web.model.sec.User;
import com.thinkit.bigdata.web.model.sec.UserExample;
import com.thinkit.bigdata.web.model.sys.SysOperateLog;
import com.thinkit.bigdata.web.service.log.SystemOperateLogService;
import com.thinkit.bigdata.web.service.sec.LoginErrorService;
import com.thinkit.bigdata.web.service.sec.PasswordCheckService;
import org.apache.log4j.Logger;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.*;

/**
 * 用户控制器
 *
 * @author StarZou
 * @since 2014年5月28日 下午3:54:00
 **/
@Controller
@RequestMapping(value = "/user")
public class LoginController extends SessionManage {

    private Logger logger = Logger.getLogger(LoginController.class);

    @Resource
    private SystemOperateLogService systemOperateLogService;

    @Resource
    private LoginErrorService loginErrorService;

    @Resource
    private PasswordCheckService passwordCheckService;

    /**
     * 用户登录
     *
     * @param user   -
     * @return -
     */
    @ResponseBody
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public HtmlResult login(User user, HttpServletRequest request) {
        try {
            Subject subject = SecurityUtils.getSubject();
            // 已登录则 跳到首页
            if (subject.isAuthenticated()) {
                return HtmlResult.success("用户已登录");
            }
            // 验证码校验
            String loginImgCode = (String) subject.getSession().getAttribute("loginImgCode");
            logger.info("登录时输入的图片验证码：" + user.getLoginimgcode() + "，session中验证码：" + loginImgCode);
            if (StringUtil.isEmpty(loginImgCode) || !loginImgCode.equalsIgnoreCase(user.getLoginimgcode())) {
                return HtmlResult.failure("验证码输入有误");
            }
            // 解密
            String password = RSAUtil.decrypt(user.getPassword(), (String) subject.getSession().getAttribute(RSAUtil.PRIVATE_KEY));
            subject.getSession().removeAttribute(RSAUtil.PRIVATE_KEY);
            // 身份验证
            subject.login(new UsernamePasswordToken(user.getUsername(), password));
            // 验证成功在Session中保存用户信息
            User authUserInfo = userService.selectByUsername(user.getUsername());
            // 判断是否已被锁定
            if (1 == authUserInfo.getIsdelete()) {
                subject.logout();
                return HtmlResult.failure("账号已被锁定，请联系管理员");
            } else if (2 == authUserInfo.getIsdelete()) {
                subject.logout();
                return HtmlResult.failure("账号已过期，请联系管理员");
            } else if (3 == authUserInfo.getIsdelete()) {
                subject.logout();
                return HtmlResult.failure("该账号已注销");
            }
            HttpSession session = request.getSession();
            // 设置session中相关信息
            loginSessionSet(authUserInfo);
            // 日志记录
            SysOperateLog log = new SysOperateLog();
            log.setUserid(authUserInfo.getId());// 设置操作人信息
            log.setUsername(authUserInfo.getUsername());
            log.setOpttime(new Date());
            log.setResourcename("用户登录");// 设置操作信息
            log.setContent(authUserInfo.getUsername() + "登录系统");
            systemOperateLogService.insertSelective(log);
            // 判断是不是首次登录 或者重置密码后首次登录（此时密码设置时间为null）
            if (authUserInfo.getLastlogintime() == null || authUserInfo.getPswsettime() == null) {
                // 上次登录时间为空 则需要强制修改密码
                session.setAttribute(Constants.FIRST_LOGIN, "true");
                return HtmlResult.success("请修改初始密码");
            }
            // 设置最后登录时间和ip
            authUserInfo.setLastloginip(getIp(request));
            authUserInfo.setLastlogintime(new Date());
            // 成功登录 清空登录错误次数 错误日期暂不处理
            authUserInfo.setErrornum(0);
            userService.updateByPrimaryKeySelective(authUserInfo);
        } catch (AuthenticationException e) {
            // 身份验证失败 记录密码错误次数
            logger.error("认证错误", e);
            try {
                // 更新错误天数
                String messgae = loginErrorService.updateLoginErrorInfo(user.getUsername());
                return HtmlResult.failure(messgae);
            }catch (Exception e1){
                logger.error("更新登录信息时发生错误", e1);
                return HtmlResult.failure("账号或密码错误");
            }
        }
        return HtmlResult.success();
    }

    private String getIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (StringUtil.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个ip值，第一个ip才是真实ip
            int index = ip.indexOf(",");
            if (index != -1) {
                return ip.substring(0, index);
            } else {
                return ip;
            }
        }
        ip = request.getHeader("X-Real-IP");
        if (StringUtil.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
            return ip;
        }
        return request.getRemoteAddr();
    }

    /**
     * 发送短信验证码
     *
     * @param request
     * @param userName
     * @param mobileno
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/pass/sendSmsCode", method = RequestMethod.GET)
    public HtmlResult smsCode(HttpServletRequest request, @RequestParam("userName") String userName, @RequestParam("mobileno") String mobileno) {
        logger.info("用户" + userName + "正在获取手机号" + mobileno + "的短信验证码");
        // 先进行校验 判断对应的用户名和手机号有没有权限获取验证码
        UserExample example = new UserExample();
        example.createCriteria().andUsernameEqualTo(userName).andMobilenoEqualTo(mobileno);
        List<User> userList = userService.selectByExample(example);
        if (userList.isEmpty()) {
            return HtmlResult.failure("该用户名与手机号对应的用户不存在，请核对后重试");
        }
        // 随机生成短信验证码
        String smsCode = String.valueOf(new Random().nextInt(899999) + 100000);
        // 保存验证码到session中
        saveSmsCodeInSession(request, mobileno, smsCode);

        if (FtpUtil.sendMobileMes(mobileno, "短信验证码为" + smsCode)){
            return HtmlResult.success("验证码发送成功");
        }
//        // 生成发送post请求的参数
//        JSONObject jsonObject = new JSONObject();
//        String[] rcvMobiles = new String[]{mobileno};
//        jsonObject.put("rcvMobiles", rcvMobiles);
//        // TODO 短信内容需要调整
//        jsonObject.put("content", "短信验证码为" + smsCode);
//        String postParam = jsonObject.toJSONString();
//        try {
//            // 请求url
//            String smsUrl = ConfigHelper.getString("sms.url");
//            // 请求返回的结果
//            String postResult = HttpUtil.sendJsonHttpPost(smsUrl, postParam);
//            logger.info("发送短信验证码返回结果信息" + postResult);
//            int code = Integer.parseInt(postResult);
//            if (code == 0) return HtmlResult.success("验证码发送成功");
//            if (code == -9999) return HtmlResult.failure("验证码发送失败");
//        } catch (Exception e) {
//            logger.error("短信验证码发送过程中出现错误", e);
//        }
        return HtmlResult.failure("验证码发送失败");
    }

    // 保存验证码到session中
    private void saveSmsCodeInSession(HttpServletRequest request, String mobileno, String smsCode) {
        SmsCodeBean smsCodeBean = new SmsCodeBean();
        // 设置有效期10分钟
        smsCodeBean.setSmsCode(smsCode);
        Calendar c = Calendar.getInstance();
        c.setTime(new Date());
        int availableSecond = ConfigHelper.getInt("sms.available.second", 600);
        c.add(Calendar.SECOND, Integer.valueOf(availableSecond));
        smsCodeBean.setExpireDate(c.getTime());
        // 保存在session中
        request.getSession().setAttribute(mobileno, smsCodeBean);
    }

    /**
     * 强制修改初始密码页 忘记密码页 修改密码
     * 此时数据库sys_user表中必须更新上次登录时间lastlogintime 密码设置时间pswsettime
     * @param request
     * @param user
     */
    @ResponseBody
    @RequestMapping(value = "/pass/resetPass", method = RequestMethod.POST)
    public HtmlResult resetPassword(HttpServletRequest request, User user) {
        if (StringUtil.isEmpty(user.getUsername())) return HtmlResult.failure("用户名不能为空");
        if (StringUtil.isEmpty(user.getMobileno())) return HtmlResult.failure("手机号不能为空");
        if (StringUtil.isEmpty(user.getSmscode())) return HtmlResult.failure("手机验证码不能为空");
        // 获取session中的验证码
        Session session = SecurityUtils.getSubject().getSession();
        SmsCodeBean smsCodeBean = (SmsCodeBean) session.getAttribute(user.getMobileno());
        if (smsCodeBean == null || smsCodeBean.getExpireDate().before(new Date()))
            return HtmlResult.failure("短信验证码已过期");
        if (!smsCodeBean.getSmsCode().equals(user.getSmscode())) return HtmlResult.failure("短信验证码输入有误");
        // 短信验证码校验通过移除验证码
        session.removeAttribute(user.getMobileno());
        // 获取用户
        UserExample example = new UserExample();
        example.createCriteria().andUsernameEqualTo(user.getUsername()).andMobilenoEqualTo(user.getMobileno());
        List<User> userList = userService.selectByExample(example);
        if (userList.isEmpty()) return HtmlResult.failure("用户名与手机号不对应");
        try {
            // 解密
            String password = RSAUtil.decrypt(user.getPassword(), (String) session.getAttribute(RSAUtil.PRIVATE_KEY));
            session.removeAttribute(RSAUtil.PRIVATE_KEY);
            // 重置密码
            User u = new User();
            u.setId(userList.get(0).getId());
            Md5Hash md5Hash = new Md5Hash(password, "加盐", 2);
            u.setPassword(md5Hash.toString());
            Date date = new Date();
            // 设置密码设定时间
            u.setPswsettime(new Date());
            if ("true".equals(session.getAttribute(Constants.FIRST_LOGIN))) {
                session.removeAttribute(Constants.FIRST_LOGIN);
                SecurityUtils.getSubject().logout();
                u.setLastloginip(getIp(request));
                // 首次登陆修改密码 保证下次登录时 不会再要求修改密码
                u.setLastlogintime(date);
            }
            // 更新用户密码
            return passwordCheckService.updatePassword(u);
        } catch (Exception e) {
            logger.error("密码重置过程中出现错误", e);
        }
        return HtmlResult.failure("密码重置失败");
    }

    /**
     * 用户登录成功跳转
     *
     * @return -
     */
    @RequestMapping(value = "/logins", method = RequestMethod.GET)
    public String login() {
        return "redirect:/";
    }

    /**
     * 用户登出
     *
     * @param session -
     * @return -
     */
    @RequestMapping(value = "/logout", method = RequestMethod.GET)
    public String logout(HttpSession session) {

        User user = (User) session.getAttribute("userInfo");

        SysOperateLog log = new SysOperateLog();
        // 设置操作人信息
        log.setUserid(user.getId());
        log.setUsername(user.getUsername());
        log.setOpttime(new Date());
        // 设置操作内容
        log.setResourcename("用户注销");
        log.setContent(user.getUsername() + "注销登录");
        systemOperateLogService.insertSelective(log);

        session.removeAttribute("userInfo");
        session.removeAttribute("userIds");
        session.removeAttribute("userIdentity");
        session.removeAttribute("areaInfo");
        // 登出操作
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "login";
    }

    /**
     * 生成密匙对 并返回公匙信息
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/pass/key", method = RequestMethod.GET)
    public String getPublicKey(){
        // 生成密匙对
        Map<Integer, String> keyMap = RSAUtil.genKeyPair();
        Session session = SecurityUtils.getSubject().getSession();
        // session中保存私匙 用来解密
        session.setAttribute(RSAUtil.PRIVATE_KEY, keyMap.get(1));
        // 返回公匙给前端 用于加密
        return keyMap.get(0);
    }

    /**
     * 用户修改密码（项目原始遗留代码 好像未使用noted by lhl）
     *
     * @return -
     */
    @RequestMapping(value = "/passwdUpdate", method = RequestMethod.GET)
    public String passwdUpdate() {
        return "userMessage";
    }


    /**
     * 基于角色 标识的权限控制案例
     */
    @RequestMapping(value = "/admin")
    @ResponseBody
//    @RequiresRoles(value = RoleSign.ADMIN)
    public String admin() {
        return "拥有admin角色,能访问";
    }

    /**
     * 基于权限标识的权限控制案例
     */
    @RequestMapping(value = "/create")
    @ResponseBody
//    @RequiresPermissions(value = PermissionSign.USER_CREATE)
    public String create() {
        return "拥有user:create权限,能访问";
    }
}
